import javax.swing.*;
import javax.swing.Timer;
import java.awt.event.*;
import java.util.*;

public class Snake extends JFrame implements ActionListener {
    JFrame frame;
    static JPanel panel;
    Head head;
    JLabel candy;
    static ArrayList<Tail> tailArray = new ArrayList<Tail>();

    public static void main(String[] args) {
        final Snake snake = new Snake();
        snake.createBoard();

        Timer t = new Timer(1000 / 30, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                snake.checkMovement();
            }
        });
        t.start();
    }

    // Initializes the UI & I/O
    public void createBoard() {
        frame = new JFrame();
        panel = new JPanel();
        head = new Head(panel.getWidth() / 2, panel.getHeight() / 2);

        addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.getKeyCode()) {
                    case KeyEvent.VK_LEFT:
                        head.direction = "Left";
                        break;
                    case KeyEvent.VK_RIGHT:
                        head.direction = "Right";
                        break;
                    case KeyEvent.VK_UP:
                        head.direction = "Up";
                        break;
                    case KeyEvent.VK_DOWN:
                        head.direction = "Down";
                        break;
                }
            }
        });

        frame.setContentPane(panel);
        frame.setSize(500, 500);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    // (Re)creates a candy in a random spot on the board
    public void createCandy() {
        if (candy == null)
            candy = new JLabel();

        candy.setLocation((new Random().nextInt(panel.getWidth())), (new Random().nextInt(panel.getHeight())));

        for (int i = 0; i == (tailArray.size() - 1); i++) {
            if (candy.getLocation() == tailArray.get(i).getLocation()) {
                candy.setLocation((new Random().nextInt(panel.getWidth())), (new Random().nextInt(panel.getHeight())));
                i = 0;
            }
        }
    }

    // Checks to see if anything is Out Of Bounds
    public void checkOOB(int i, Boolean tail) {
        // Checks OOB for Head with same procedure as Tail, but with dif. var names
        if (!tail) {
            if (head.getX() < 0)
                head.setLocation(panel.getWidth(), head.getY());
            if (head.getY() < 0)
                head.setLocation(head.getX(), panel.getHeight());
            if (head.getX() > panel.getWidth())
                head.setLocation(0, head.getY());
            if (head.getY() > panel.getHeight())
                head.setLocation(head.getX(), 0);
        }

        // Checks for OOB for Tail (checks for the instance in the array based on what number 'i' is
        if (tailArray.get(i).getX() < 0)
            tailArray.get(i).setLocation(panel.getWidth(), tailArray.get(i).getY());
        if (tailArray.get(i).getY() < 0)
            tailArray.get(i).setLocation(tailArray.get(i).getX(), panel.getHeight());
        if (tailArray.get(i).getX() > panel.getWidth())
            tailArray.get(i).setLocation(0, tailArray.get(i).getY());
        if (tailArray.get(i).getY() > panel.getHeight())
            tailArray.get(i).setLocation(tailArray.get(i).getX(), 0);
    }

    // Checks OOB, then moves the head, then the tail
    public void checkMovement() {
        // Checks for OOB with the Tail loop first, then the Head separately
        for (int i = 0; i == (tailArray.size() - 1); i++)
            checkOOB(i, true);
        checkOOB(0, false);

        // Checks Tail movement
        for (int i = 0; i == tailArray.size() - 1; i++) {
            Tail tail = tailArray.get(i);
            if (tail.direction.equals("Up"))
                tail.setLocation(tail.getX(), tail.getY() - 1);
             else if (tail.direction.equals("Down"))
                tail.setLocation(tail.getX(), tail.getY() + 1);
             else if (tail.direction.equals("Left"))
                tail.setLocation(tail.getX() - 1, tail.getY());
             else if (tail.direction.equals("Right"))
                tail.setLocation(tail.getX() + 1, tail.getY());

        }

        // Checks head movement
        if (head.direction.equals("Up"))
            head.setLocation(head.getX(),  head.getY() - 1);
         else if (head.direction.equals("Down"))
            head.setLocation(head.getX(),  head.getY() + 1);
         else if (head.direction.equals("Left"))
            head.setLocation(head.getX() - 1,  head.getY());
         else if (head.direction.equals("Right"))
            head.setLocation(head.getX() + 1,  head.getY());


        // Checks to see if the head ran over candy
        if (head.getLocation() == candy.getLocation()) {
            new Tail(tailArray, head);
            createCandy();
        }
    }

    // Gets the max of an array (the size - 1)
    public static Tail getMax(ArrayList<Tail> arrayList) {
        return arrayList.get(arrayList.size() - 1);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        checkMovement();
    }
}

class Tail extends JLabel {
    String direction;

    public Tail(ArrayList<Tail> tailArray, Head head) {
        super();
        Snake.panel.add(this);
        setOpaque(true);
        setVisible(true);
        setText("=");

        int x = 0;
        int y = 0;
        // If the only Tail in the array is this new one, location is based off of the Head.
        if (tailArray.size() == 0) {
            if (head.direction.equals("Up")) {
                x = head.getX();
                y = head.getY() + 1;
            }
            if (head.direction.equals("Down")) {
                x = head.getX() + 1;
                y = head.getY();
            }
            if (head.direction.equals("Left")) {
                x = head.getX() + 1;
                y = head.getY();
            }
            if (head.direction.equals("Right")) {
                x = head.getX() - 1;
                y = head.getY();
            }

            // If there are others, the location goes off of the last one.
        } else {
            Tail lastTail = Snake.getMax(tailArray);
            if (lastTail.direction.equals("Up")) {
                x = lastTail.getX();
                y = lastTail.getY() + 1;
            }
            if (Snake.getMax(tailArray).direction.equals("Down")) {
                x = lastTail.getX() + 1;
                y = lastTail.getY();
            }
            if (lastTail.direction.equals("Left")) {
                x = lastTail.getX() + 1;
                y = lastTail.getY();
            }
            if (lastTail.direction.equals("Right")) {
                x = lastTail.getX() - 1;
                y = lastTail.getY();
            }
        }

        setLocation(x, y);

        Snake.tailArray.add(Snake.tailArray.size(), this);
        this.setName("tail" + Snake.tailArray.size());
    }
}

class Head extends JLabel {
    String direction;

    public Head(int x, int y) {
        super();
        Snake.panel.add(this);
        setOpaque(true);
        setVisible(true);
        setText("O");
        setLocation(x, y);
    }
}